<?php
/** @file
 * The only required php file in the linkz package.
 */
 
/** @mainpage linkZerver Documentation  
 * Hello!  And welcome to the Linkz Zerver, version 0.1alpha!  
 * 
 * Linkz Zerver is designed to be a easy to use and compact zerver for peer2peer 
 * linkz on the linkz network.
 * 
 * A zerver isn't just an ordinary server.  A zerver:
 * -# Stores configuration
 * -# Generates JavaScripts for sites (or handles includes) with the linkz
 * -# Communicates with other Zervers
 * -# Provides an Administration interface
 * -# and Manages your local sites database, all in one PHP script! 
 * 
 * So, since your Zerver is going to do all that for you, you'll want to treat 
 * it nicely :o)  You'll want to customize the config options futher down...
 *
 * @page license linkZerver is released under the GPL
 * 
 * The Linkz Zerver script
 * Copyright (C) 2006  Robin Monks <devlinks@gmail.com>
 * 
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
 *   USA. 
 *   
 *  The full text of the GPL is at http://www.gnu.org/copyleft/gpl.html  
 */    

/////////////////////////////////////////
/// @name Configuration options
/// @{
/// Settings needed by Linkz to start.  Settings that are not vital to starting
/// the server are instead placed in the database.
/// @todo Move many of these to the database and make a GUI for the settings.
/////////////////////////////////////////
 
/** What servers can this zerver serve JavaScript to. Set this to a wildcard of 
   the domains Zerver should allow to access the JavaScript files, this will not 
   affect server side includes or what zervers you'll exchange with. */
$restrict = "*.yourdomain.com*";

/** The location of the SQLite database.  One will be created if it doesn't 
   exist.  The file must be chmod'ed 777 to write data to the database on 
   unix/linux servers.  The folder must be chmoded 777 to create the database if 
   it doesn't already exist, you can create a blank file and chmod it 777 if 
   your server has problems. */
$dba_path = "linkz.db";
$script_name = "linkz.php"; ///< The filename of the linkZerver, only needs to be changed if you rename it.

global $dba_path, $script_name; 

$def_site_name = "My Homepage"; ///< The default name of your site. Max 20 chars.
$def_site_desc = "It's my own little place on the web."; ///< Site's description. No more than 50 chars
/** Up to 10 keywords that describe your site's content.  These keywords decide
   what linkz will display in your linkzbar, what linkz other zervers will share
   with you and has some bearing on what site will choose to display you linkz.
   This system works best if you are exact about your keywords. */
$def_keywords = Array('site', 'page', 'home', 'little', 'web');

/** Setting this to TRUE will ask a few pre-set sites to share linkz based on the
   keywords you set above.  This "seeds" your linkz for you, so you don't have
   to specify any sites to begin with.  Set to false to set some sites in the
   admin interface or to wait for a site to link to you first. @see $def_keywords,$seed_dirs*/
$seed_linkz = TRUE;
/** You don't need to set this, it's a "default" list of sites in XML format.
   The top 5 from the first link the server can access, based on your keywords
   will be added to your linkz list.  After your sites has it's first 3 links, 
   this list is no longer used. @see $def_keywords,$seed_linkz*/
$seed_dirs = Array('', '');

$def_num_ads = 5; ///< How many linkz to show in a LinkzBar by default?

/// @name Theming options
/// @{
/// Settings related to theming your linkz.
/// @todo Move to DB, allow multiple themes, make a nice GUI for this.

$def_bordercolor = '#276484'; ///< The default LinkzBar border color!
$def_background = '#D3E7F2'; ///< And the defult background color...
$def_textcolor = '#000000'; ///< Default text color.
$def_titlecolor = '#133242'; ///< Default site name color.
$def_linkcolor = '#008000'; ///< Default url color.
$def_linkbackfontcolor = '#FFF'; ///< "Free Linkz" text color.

/// @} End of "Theming options".

/* CONGRATULATIONS! You're done with the configuration :)
   To add linkz to your site via JavaScript add the following code:
   ToBeDone
   To add Linkz to your site via Includes add the following code to a PHP page:
   ToBeDone
   and this code to a server side includes supported page:
   ToBeDone
   */
   
$linkz_ver = "0.1"; ///< Version of Linkz installed, used for updates.
$linkz_ver_text = "0.1 Alpha"; ///< The nice looking version of the..er..version.
$linkz_api_min = "0.1"; ///< These are the minimum a maximum versions of the Linkz
$linkz_api_max = "0.1"; ///< API this Zerver supports, sent in pings.
   
global $linkz_ver, $linkz_ver_text, $linkz_api_min, $linkz_api_max;  
/// @} End of "Configuration options". 

/// @name Error Cache @{
/// The global error cache allows non-fatal errors to be printed "pretty" in the theme.
$error_cache = '';
global $error_cache;
/// @} End of "Error cache"

/** DB Functions, basically fancy wrappers ;o) */
class linkz_db {
  /** Creates an SQLite object based on configuration. */
  function open_db() {
    global $dba_path;
    if (!file_exists($dba_path)){touch($dba_path);}  // init new DB.
    if (!$linkz_db = new SQLiteDatabase($dba_path,0777,$sqlite_error)) {
      if(isset($sqlite_error)){
      $additional = "Additionally, SQLite provided the following information reguarding this error:<br><pre>";
      $additional .= $sqlite_error;
      $additional .= "</pre>";
      } else {$additional = 'Linkz cannot continue, please check Database settings.';}
    die('<h1>DB Error Occured</h1>'.$additional);
    }
    return $linkz_db;
  }
  /** Sets variables. @todo Make set_var work, and make get_var. */
  function set_var($varname) {
    $dbid = open_db();
  }
  /** Bundled PHP SQL extension doesn't have IF NOT EXISTS, so we use this instead.
     @param sqlite SQLite database, passed by reference.
     @param table  Table name to search for
     @return BOOLEN if table exists or not.
  */
  function table_exists(&$sqlite, $table) {
    $result = $sqlite->query("SELECT name FROM sqlite_master WHERE type='table' AND name='$table'");
    return $result->numRows() > 0;
  }
}

/** User management functions. */
class linkz_user {
  /** Prints and verifies login information and creation.  */
  function login() {
    $return = "";
    if (isset($_POST['op'])) {$doop = $_POST['op'];}
    elseif (isset($_GET['op'])) {$doop = $_GET['op'];}
    else {$doop = 'default';}
    $first_run = FALSE;
    $first_login = FALSE;
  
    global $linkz_db;
    $user_e = $linkz_db->arrayQuery('SELECT * from user;');
    if (empty($user_e)){
      $first_run = TRUE;
    }
  
    switch ($doop) {
      case "Login":
        $return .= "<h1>Processing Login...</h1>";
        if ($first_run){
          if(isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '' && isset($_POST['confirm']) && $_POST['confirm'] != ''){ // better safe than sorry :o)
            $a_username = htmlspecialchars(urldecode($_POST['username']));
            $a_password = htmlspecialchars(urldecode($_POST['password']));
            $a_confirm = htmlspecialchars(urldecode($_POST['confirm']));
            if (strlen($a_username) > 4 && strlen($a_password) > 4) {
              if ($a_password == $a_confirm){
                // Make new account
                $b_password = md5($a_password);
              
                $linkz_db->queryExec('DELETE FROM user'); //for resetting pass
                $linkz_db->queryExec("INSERT INTO user VALUES ('".serialize($a_username)."','".serialize($b_password)."')");

                $return .= "<h2>Please wait while the Zerver launches for the first time...</h2>";
                $first_login = TRUE;
              } else {
                $return .= "<p style='color:red;'><b><u><i>Password fields do not match.  Please enter the same password in both fields.</i></u></b></p>\n";
              }
            } else {
              $return .= "<p style='color:red;'><b><u><i>Usename and password fields must be 5 characters or longer.</i></u></b></p>\n";
            }
          } else {
            $return .= "<p style='color:red;'><b><u><i>Please fill in all fields.</i></u></b></p>\n";
          }
        }
        if((isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') && (!$first_run || $first_login)){
          $a_username = htmlspecialchars(urldecode($_POST['username']));
          $a_password = htmlspecialchars(urldecode($_POST['password'])); 
        
          // We can already be sure 'pass' and 'user' exist in the DB via first_*.
          $user_e = $user_e[0];
          $db_username = unserialize($user_e['username']);   
          $db_password = unserialize($user_e['password']);  
        
          if (strtolower($a_username) == strtolower($db_username) && md5($a_password) === $db_password) {
            $_SESSION['username'] = serialize($db_username);
            $_SESSION['authtime'] = serialize(time());
            $_SESSION['authhash'] = serialize(md5($a_username . unserialize($_SESSION['authtime']) . md5($a_password)));
            global $script_name;
            list ($url, $query_string) = explode ('?', $_SERVER ['REQUEST_URI'], 2);
            $urldir = dirname ($url . 'x');
            header("Location:$urldir/$script_name");
            break;
          } else {
            $return .= "<p style='color:red;'><b><u><i>Username and/or password does not match database values.</i></u></b></p>\n";
            $return .= "<p style='color:red;'>DB had '$db_username' and '$db_password', you had '$a_username' and '".md5($a_password)."'.";
          }
        } else {
          if (!$first_run || $first_login) {
            $return .= "<p style='color:red;'><b><u><i>Please fill in all fields.</i></u></b></p>\n";
          }
        }

      default:
        $return .= "<h1>Admin Login</h1>\n";
        if ($first_run){
          $return .= "<p>Welcome to Linkz Zerver!  You'll need to create an account to get started.  Please enter your desired username and password below:</p>";
        } else {
          $return .= "<p>Please enter your username and password below to login:</p>\n";
        }
        global $script_name;
        $return .= "<form method='POST' action='$script_name'>\n";
        $return .= "<p>Username: <input type='text' name='username' size='20' /></p>\n";
        $return .= "<p>Password: <input type='password' name='password' size='20' /></p>\n";
        if ($first_run){
                $return .= "<p>Confirm: <input type='password' name='confirm' size='20' /></p>\n";
        }      
        $return .= "<p><input type='submit' name='op' value='Login' /></p>\n";
    }
  return $return;
  }
  /** Checks if a user is logged in and valid with a highly picky if statement. */
  function logged_in() { // Returns true if a user is logged in and valid.
    global $linkz_db;
    // This if statement was too complex to be a one-liner, your eyes will concur.
    if (
         (isset($_SESSION['username']) 
           && isset($_SESSION['authhash']) 
           && isset($_SESSION['authtime'])) 
         && 
         ($_SESSION['username'] != '' 
           && $_SESSION['authhash'] != '' 
           && $_SESSION['authtime'] != '') 
         && 
         (unserialize($_SESSION['authtime']) > time() - 1800) 
         &&
         ($user_e = $linkz_db->arrayQuery('SELECT * from user'))
         && 
         (!empty($user_e)) 
         &&
         ($user_e = $user_e[0])
         && 
         (md5(unserialize($user_e['username']) . unserialize($_SESSION['authtime']) . unserialize($user_e['password'])) === unserialize($_SESSION['authhash'])
         )
       ) {
      return TRUE; // Wow, they are the genuine article :o)
    }
    return FALSE;
  }
  /** Makes sure the user is logged in, to be called inline.  Includes recovery 
    if output has already begun. */
  function require_logged_in() {  //  
    if (!linkz_user::logged_in()) {
      if (!headers_sent()) {
        global $script_name;
        list ($url, $query_string) = explode ('?', $_SERVER ['REQUEST_URI'], 2);
        $urldir = dirname ($url . 'x');
        header("Location:$urldir/$script_name?do=login");    
      } else {
        // Whoops!  We started printing content already, let's stop the madness.
        echo "<p>Please login to continue.</p>\n";
        echo login();
        die();
      }
    }
  }
  /** Remove login cookie and session to log user out. */
  function logout() {
    // We let this fire even if the user isn't logged in to clear cookies.
    $_SESSION = array();
    if (isset($_COOKIE[session_name()])) {
      setcookie(session_name(), '', time()-42000, '/');
    }
    session_destroy();
  }
}

/** All zerver-related functions, like ping, pong and sync. */
class linkz_zerver {
  function pong() {
    global $def_site_name, $def_site_desc, $def_keywords, $linkz_api_min, $linkz_api_max;
    return Array("site" => Array("name" => $def_site_name, "desc" => $def_site_desc, "ping" => "pong", "keywords" => serialize($def_keywords)), "api" => Array('min' => $linkz_api_min, 'max' => $linkz_api_max));
  }
}

/** Makes an array or string into an XML document. */
function print_r_xml($arr,$first=true) {
  $output = "";
  if ($first) $output .= "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n<data>\n";
  foreach($arr as $key => $val) {
   if (is_numeric($key)) $key = "arr_".$key; // <0 is not allowed
   switch (gettype($val)) {
     case "array":
       $output .= "<".htmlspecialchars($key)." type='array' size='".count($val)."'>".
         print_r_xml($val,false)."</".htmlspecialchars($key).">\n"; break;
     case "boolean":
       $output .= "<".htmlspecialchars($key)." type='bool'>".($val?"true":"false").
         "</".htmlspecialchars($key).">\n"; break;
     case "integer":
       $output .= "<".htmlspecialchars($key)." type='integer'>".
         htmlspecialchars($val)."</".htmlspecialchars($key).">\n"; break;
     case "double":
       $output .= "<".htmlspecialchars($key)." type='double'>".
         htmlspecialchars($val)."</".htmlspecialchars($key).">\n"; break;
     case "string":
       $output .= "<".htmlspecialchars($key)." type='string' size='".strlen($val)."'>".
         htmlspecialchars($val)."</".htmlspecialchars($key).">\n"; break;
     default:
       $output .= "<".htmlspecialchars($key)." type='unknown'>".gettype($val).
         "</".htmlspecialchars($key).">\n"; break;
   }
  }
  if ($first) $output .= "</data>\n";
  return $output;
}

/** Turn an XML document into an array */
function xml2array($data) {
  $p = xml_parser_create();
  xml_parser_set_option($p, XML_OPTION_SKIP_WHITE, 1);
  xml_parse_into_struct($p, $data, &$vals, &$index);
  xml_parser_free($p);
  $tree = array();
  $i = 0;
  $tree = GetChildren($vals, $i);
  return $tree;
}

function GetChildren($vals, &$i) {                 
  $children = array();
  if ($vals[$i]['value']) array_push($children, $vals[$i]['value']);
  $prevtag = "";
  while (++$i < count($vals))
  {     
    switch ($vals[$i]['type'])
    {         
        case 'cdata':
          array_push($children, $vals[$i]['value']);
          break;
        case 'complete':                     
          $children[ strtolower($vals[$i]['tag']) ] = $vals[$i]['value'];           
          break;
         case 'open':                               
           $j++;
           if ($prevtag <> $vals[$i]['tag']) {
                   $j = 0;   
                   $prevtag = $vals[$i]['tag'];
           }                             
           $children[ strtolower($vals[$i]['tag']) ][$j] = GetChildren($vals,$i);
           break;
         case 'close':         
           return $children;
    }
  }   
}

/** Handles linkz management pages and forms. */
function manage_linkz() {
  linkz_user::require_logged_in(); // no nasties allowed ;)
  if (isset($_POST['op']) && isset($_POST['op']) != '') {$doop = $_POST['op'];}
  elseif (isset($_GET['op']) && isset($_GET['op']) != '') {$doop = $_GET['op'];}
  else {$doop = 'default';}
  $return = "";
  global $script_name;
  
  switch ($doop) {
    case "add":
      $return .= "<h1>Add site</h1>\n";
      $return .= "<form method='POST' action='$script_name?do=linkz'>";
      $return .= "<p>If you know the direct location of the zerver ping URL, you can usually find it by clicking the \"Free Linkz\" link under a LinkzBar, enter it here (URL will end in do=ping):</p>\n<p><input type='text' name='ping_url' /></p>\n";
      $return .= "<p>If you don't know the ping URL of the site, but you know the site uses Linkz enter the site URL below in format http://sitetolinkto.com/, make sure to include the trailing slash (/):</p>\n<p><input type='text' name='base_url' /></p>\n";
      $return .= "<p><input type='submit' name='op' value='Add Site' /></p>\n";
      $return .= "</form>\n";
      $return .= "<p><a href='$script_name?do=linkz'>Go Back</a></p>";
      break;
    case "Add Site":
      if (isset($_POST['ping_url']) && $_POST['ping_url'] != '') {
        $the_site = strip_tags($_POST['ping_url']);
        $return .= sync_link($the_site);
      } elseif (isset($_POST['base_url']) && $_POST['base_url'] != '') {
        $the_site = strip_tags($_POST['base_url']);
        $return .= "<p>Normally, we'd crawl $the_site for Linkz now.</p>";      
      } else {
        $return .= "<p>Some or all fields were left blank, no sites added.</p>";
      }
    default:
      $return .= "<h1>Manage Linkz</h1>\n";
      $return .= "<p>On this page, you can manage what sites your LinkzBar and other Linkz Zerver powered services link to, and genorate linkz from.  Go to the settings page to set filters for what sites can link exchange with your site.</p>\n";
      
      global $linkz_db;
      $results = $linkz_db->arrayQuery('SELECT * from sites');
      if(!empty($results)) {
      
      } else {
        $return .= "<p>Currently, no sites exist in the database, please add some using the links below.</p>";
      }
      $return .= "<p style='text-align:center;'><a href='$script_name?do=linkz&op=add'>Add site</a> | <a href='$script_name?do=linkz&op=add_by_word'>Add site(s) by keywords</a> | <a href='$script_name?do=linkz&op=sync'>Sync sites</a> | <a href='$script_name?do=linkz&op=sync'>Clean sites</a></p>";
      break;
  
  }
return $return;
}

//
// Linkz Parser
//

/** Use CURL to retrive a file.  
  @param pageSpec URL of file to fetch.
  @return         Contents of file.
  @todo Make this work with fopen as well. */
function get_page($pageSpec) {
   $ch = curl_init($pageSpec);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   $tmp = curl_exec ($ch);
   curl_close ($ch);
   return $tmp;
} 

/** Recursively search an array for a value in an array.
  @param needle       Value to search for.
  @param haystack     Array to search for value in.
  @param key_lookin   Only look in keys with this name.
  @return             Path to needle in array haystack. */
function array_search_recursive($needle, $haystack, $key_lookin="") {
$path = NULL;
   if (!empty($key_lookin) && array_key_exists($key_lookin, $haystack) && $needle === $haystack[$key_lookin]) {
   $path[] = $key_lookin;
   } else {
       foreach($haystack as $key => $val) {
           if (is_scalar($val) && $val === $needle && empty($key_lookin)) {
           $path[] = $key;
           break;
           }       
           elseif (is_array($val) && $path = array_search_recursive($needle, $val, $key_lookin)) {
           array_unshift($path, $key);
           break;
           }
       }
   }
return $path;
}

/** Sync the database with a link, this function can update, add and delete.
  $link_url is considered clean when it's recieved, and is used directly.
  @param link_url URL of the target sites linkZerver ping URL.
  @return         Output to be printed to user. */
function sync_link($link_url) {
  linkz_user::require_logged_in();
  $pong = xml2array(get_page($link_url. '&ping=' .serialize(linkz_zerver::pong())));
  if($pong['site'][0]['ping'] == 'pong') {
    global $linkz_api_min, $linkz_api_max;;
    if($pong['api'][0]['max'] <= $linkz_api_max || ($pong['api'][0]['min'] <= $linkz_api_max && $pong['api'][0]['min'] >= $linkz_api_min)) { 
      $return .= '<h3>The site '.$pong['site'][0]['name'].' was added</h3>';
      $return .= "<ul><li>Site Name: ".$pong['site'][0]['name'];
      $return .= "</li><li>Site URL: ".$link_url;
      $return .= "</li><li>Site Description: ".$pong['site'][0]['desc'];
      $return .= "</li></ul>";
    }  
  }

  return $return;
/*  $dbid = open_db('w');
  if (dba_exists('sites', $dbid)) {
    $sites = unserialize(dba_exists('sites', $dbid));
    if ($point = array_search_recursive($link_url, $sites)) {
      $site = $sites[$point[0]];
    }
  }*/


}

//////////////////////////
/// @name Admin styles
/// @{
/// Stlyes for admin pages
//////////////////////////

/// Stylesheet for admin pages.
$admin_stylesheet=<<<EOS
body {
	margin:0;
	padding:0;
	background:#6DA18B url(%2F%2F%2F%2FLs4fLs4PLs3%2FLs3GadhsbGxv%2F%2F%2Fr%2B%2Fv%2F%2F%2B%2Fk2NcEiLcOPh4UWJbUeJbWedhlmWe2KbgmmfiGCZgd7XykmKbv7%2B%2FlKRd1qWfUiJbkyNcvLs11GRd2yhimieh%2FLs3WScgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAIAMBAAAGXEBPoQCaQDiKSmPAbDINgKh0So1aEoeqdsvter%2FgsHhMLpvP6LR6zW673%2FC4vMygCO6BvH7P7%2Fv%2FgIGCg4SFhoeIiYqLf3eOj48DHwSUBBsIWw4ZCxoXGBEPEh1BADs%3D) repeat-y top center;
	font-size:78%;
	font-family:Helvetica, Arial, Sans-serif;
}	
a {
	color:#4290B8;
}
a:hover {
	color:#59806F;
	text-decoration:none;
}	
h1, h2 {
	margin:0;
	padding:10px 0 0 40px;
	font-family:Tahoma, Arial, Sans-serif;
	font-weight:normal;
	font-size:1.6em;
	color:#333;
}
p {
	text-indent:10px;
	padding-left:10px;
}
table {
	width:100%;
	border:1px solid #ccc;
	border-collapse:collapse;
	margin-left:10px;
}
td {
	border-right:1px solid #ccc;
	border-bottom:1px solid #ccc;
}
th {
	background:#e5e5e5;
}	
#container {
	width:762px;
	margin:0 auto;
}
#header {
	background:url(data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAQEASABIAAD%2F4QAWRXhpZgAATU0AKgAAAAgAAAAAAAD%2F2wBDAA0JCgsKCA0LCgsODg0PEyAVExISEyccHhcgLikxMC4pLSwzOko%2BMzZGNywtQFdBRkxOUlNSMj5aYVpQYEpRUk%2F%2F2wBDAQ4ODhMREyYVFSZPNS01T09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0%2F%2FwAARCACmAwIDASIAAhEBAxEB%2F8QAGwAAAgMBAQEAAAAAAAAAAAAAAgMAAQQFBgf%2FxAAqEAACAgIBBQEBAQACAgMBAAAAAQIRAyExBBITQVFhFCIFMgZxI0KBUv%2FEABkBAAMBAQEAAAAAAAAAAAAAAAABAgMEBf%2FEAB8RAQEBAQADAQEBAQEAAAAAAAABEQIDEiExEwRBMv%2FaAAwDAQACEQMRAD8A39rJ2WHCafKGxlA9e3HnSaVHHRogtItVQS%2FCL1q5MSkVVcBIhJ0D4BS2MrZdIelhfsqUqGtL0LlXsIC5NsVJOhrolJlz4mxlkn7Fu0zZLGmKlj%2FCpU2EqTTGwlaA7GvRcbT4HfohtWidiChTXAfYmuSbcVhDhvRXbs0LHrRTxtBpWE6RO6mNcHXALgvY9GBUwlKwKSZTYYWmqSRfeqM7kC2wwezS5%2FAHJPYjuklyTuYYXtp3ciu7egI3J8GnFhvcuAvwT6Wk5DI4mPSilpEdsj2XmFrGkX%2FmKCSop18DTxWn6LUq4CUU0Tt%2FA0YpSpjFOlwUoXwW41yiaItZEW5AuKWyVYYer7tlqWwaLS3wANSTQzp3UwMcLXJafZKkZ378VPjpQyJRpsU8qboQ3LtAVxe2ZThrej5STYvI9EtNC574K5mJ6uly2Ck3wi3JLkrzxiaxktxkhTjJsJ9TH6LfUxZUlFsF2tci5xsqWe3oB5CpKnYGUdgSVBymhbmipqaBxbYLiG8n4A5tlRNDRGinJgPI7GQu1Ibjq9mZzZak75CwNqcfg2DittmSL9thOd8E2KlxuUotcldqbMalK9DoTa9k3nFTrT%2FAvpTw0SLk9hOT%2Bi%2BqUoNaZdJckTvkt0iaF9sQ4qNVQtOgZTphmjcNcYUKniTdoHyJ8lOf6OSwW6XPCmL%2FAJr2Oc2U8jKlqbIV%2FM0C8NDnPX%2FYBuT4ZUtLCXjrgXJcjsikhUrrgpNKaBY2k%2BUU4fgESQY4UA0ATghRACEIQAhCEAIWnT0UQAZHJJD4TejNHnZox0woaYW2MpoHG0kH3kVcDRAu9EECVEZGDfAyMIhpL0TelyAjFrQxLRGWnoVpyJYPfQboBx%2BCgqu%2F8Di7BUfpaVDoXJfBUotjk%2FpUl8CUWM7VEWxkogNUipdJKLUEynJopTaYYB%2BIF4qfAccloLvT5FtHwmq9BKSSLk0%2BBcnoc%2BkPypcC5ZRUm17Ftsqclejnn%2FBcsz%2BApv2X%2Fl8qh5id0Dzt%2Bid%2FcH2QfstYo3yPRhZfax6xxGLGqFehOWVQbfAxYh6xqw4wXAr0qcl4sdPg0xi6qi4wUUMUkZ9dKkwHi%2BFPGxveRysnarCfG7CWEJSsZFhbRJoY4gZwod3UgG7QpTsBFMtr6WrvgKpP0O0pCJR%2FAEpI0NXyTtHOhYSu4ZFWWokS3oVoh2NVouWLd0XiRpik0YddWVtOZWfJCXYmgMcHL%2FtZt7V20XHtbSoU7%2BHeCoYP8CckO10joPH3RpMz5cE60rFz2Ly5mbDPmNsxTtN2dvHadSRm6zoHkn3QpJnRx5P%2BVj14%2FwDsciTb4AbaCz45Yp0xTZ0z7GFF3yXsvyP6JlbIotbHhac5tgORSv2R7QEq5AtyCWN17L8T%2BMAS237KHvDXJXYgBOicDHGvVlLngAqM2uOB0JoBxSVlR26oA0xmmNjHYrHj4ZqgqRFq5FqDrkPtf%2Fsj7ktFqUvaI1a4K%2FRJx%2BFq0WnvYtMpxlQDjJ8mlVRUoqgnRWMcotA91PaNElT4EyjvguXU2K8ivgpyv1sKKSdhd0fgxhDiXB0wpOLYCknKkhp%2FDpx7oJoBu4driMj3JaQbwZJ04wJ2RWaxvHrgW1s6Euny44d0oOheLpZ55f8AxRsc6hXmufKMnwMxdF1GZ1jxtno%2Bk%2F4yMIJZYKzsdPghCNKKRj3%2FAKZz%2BNefBb%2BvCT6HqYX3YpUvdGamtNH0jJghODi0tnFz%2FwDj0Hm74P8Az7QuP9Uv%2Fo%2Bv89n48gQ9J1n%2FAI9UHLByvRwc3T5MM3Capo348nPf4x64vP6SQKn8JT%2BFoCQKn8I00ACMhOgVFt0HHG1yAOjkdcjFk1TExVBpCsOUy19IBogG2qSotSX0z9xO5r2Z%2BrTWnvVEUkZu%2Bi1New9R7NPcrJ3pCVKISaYryNNU0%2FZXeLa3ou6DBpikiOYF2Lnf0JBo5T0Kc2D3P2U5FyJtTyOyOVgOSB7xyJtNWSi3KzM5stZGGDT3OvYEsiAbTBaHILROSK7gKLXAy1d%2FCAu2iu5gRiYSloBJtcFvXKCw5cNjkGxyx9mZWMWOVW%2BCbDlP74vhlqSXsyf6X0OMn7FeVTpr8joJZaexEbkipJ0T6nrT5U%2FZamn7RjTZabD1Hs1p75GK%2Fplxz%2Bj4ST9k2Ycp8VaCWPYEJUxqmr2Z1cC4VtB4pXpkclTExdSYrNipcaJwikJap6J3O%2BQ47ZM%2BK%2FVUC40xtq6I1Y9TiY9GmCtGeMWkMhOpVZlfrSfGuON9t1oCUN2vQXm%2Fx22Fa7dGe1Y8LXbRojFSXBihKpUbcUtEVUZ8%2FS3K4oBdPLiR0LsjSYe1GPN%2F8p%2Fxrn%2FuHo4nhabT9Hu8mGM4tHF67%2Fi0k5wT%2FwDSOvw%2BfPnTm8vh37Hn4YHLhWNfRuKVrk6eHA8Udx2PngckrNr5vrOeJxH01PgtdPW2juR6GM42ZMmHsbTQTzSi%2BPHP8eieNmtxSChictpFe2F6ufPE%2FYieOS9HoIdOtNxTGdR%2FxyzRUopJk%2F3kp%2FxrzSg%2FgUcVbOhk6d45uLQqSS0kjWdaz9cYnzwNxKH%2FAPOxnZb9BRhFMdpSDUV6QUeaobDtkqQyONekZXrGk50EY6DePQxY6VtBPjgzvS5yzSXa9lOUQ8i7mZpKSbo05%2Bpvw5TRHK1oyNTT2woOltlepaZJgN6KlkQp5ExyJtW%2BeQZa9guQN2VIm0dJrfIyEUt8sHHilkjaRt6fpVJXJNUT11Irnm0OJ9zSaOt02P8Ayu4zY8Ki%2BDXHaRyeXvfx0%2BPnP0%2BWHHPH2yppmfFgj00n49ocn2hWmYzq%2FjW8yglOTVrQ%2Fps%2B0pMS9MpR%2FwBJom%2FTdVNMsz45NJWM8iohSSS2eW%2F53pezP3r%2FAOx6OeWpb4OP%2FwAxkjKlaN%2FBbOmXmkvLzlMpxNbjH2A1E9HXDjM4Fdm%2BdGl9oDcV6GWFKCuwy%2B5fCu78GESLSZXc6KTftgDO1%2FCC%2B5%2FSCGtbihTtMPf0FkxdU%2BAG2mFdcgyknyOJHGVBKZnv9BeWtBg1q723yGu5mWOVfTRjzKuUFhymxu9hNWK8lvTJbfsnFaKUULpey2m%2FYMkxwqqSi9IW8dh0wop%2B0PcTmkODIoM1qGuAe13xoPYerNXa%2FrK%2F03waniTIsdMejKR4WR4pGyCVbRfZb4J9lerA8MkRY38Oi8Nop4kvQe49WSMa5QTxqXof2L4Eo64D2HqzRg4tG2EFLHwhbiMi6J6qpAvAmLeJJ8GlSVAS5smWjIGMEiSgmV3bL8lof0in2xfAEp%2Fgc5K%2BBMp%2FhUKijLewlkSfJncpfCl3FYWt8c6qrCWdfTnXJE7pN%2ByfQ%2FaumuoX0KM1J6Ofjt8s1dO%2B174J65xXPWtkVQa0Jll1cSoSnLkwvFazo623ofjVx2tisCVmvHFUZd9Z8acc79AkBNJPQ3Iu3daFtb5IlXYpNr2PxO1zszvkKLa4Ffonxpbodiybox9zfLChJp8k2KdWDtBmLFmr2ao5E0SYypK1RE0y9AGPP08XtIHxf5po1z2B64H7UsjIk8SutGHq4PLPugdiWOMoNGeXTqP%2FAFL57z6nrnfjh%2BKXfUtGzE1BUkaMuFN20SOONcGvXlnURPHlSC7kHGcoRrkkY0w%2BzuZjbrSRi6vtyRbaSZzPB3p09o6vVY%2B1mWKUW2dHj7zlh3ztYJY3dBQw2%2BR2ZbbRWDUlZv7fGPr9Ph0%2FbG6HQxfhoxxUojVBJHL15K6eeIS8f%2BeBLxbNkkLlGyOelXlzskabEyj9NmeHbyZ3VHVx1sc%2FUxmnFehbizS4qwHC3pmsqLGZwYPib4NLxv6THCp02V7Yn11nXTyLjg7JK0dBQVAeKUnpaM%2F6r%2FmdhjHxrSGV2rQp3jirLjlT5Oa21vJIdGVjVOjPCcbGOcaM6uNEZ2MizJGaQ%2FHO%2FZN%2BKhva29lxi70goNMaqJ0xR1HYMvxhMkI298CNnyv%2FAA2ef6lynkbk7PTdTgbg%2B08%2F1XS5o5HrR1f57I5%2FNLYx9lLYqcXejRPHNVGti5QnH%2Fsjslc1jO4spxbHqN7YucknRUqbCWQttPgopKrIWk7ugm%2FVUACQlP4QA3SgktAPtojnsB7M5GtDNJr9Eyj9HKKXsGfatlRFhPboVOkHky%2BkKbt7KShabXDBIAOhmkuTVizJrZhvRcZtBmnK6kXF7TLlG0YsOZGhZURYuUXa0RtAyyr6D5IhlLWiLVBNr4Z45F9H42nyxWYqXU1Wi4xbG%2BNLaL0vRNp4GOPY2MFRSobGmiLVSAcaWgXH6OYDTsUp2FOCorsGNFVsqVOAcCdtDFFt8DfDaFepDkZdL2WnFobLp3Yt4JIcspWWKeNPgB46JU48h7fI9Ip4yvD%2BDQtUPRjN4fwrwmlsFqxzosZniSL7Yr0O7LDhgsL0XqzJK6ofCGuBqwJPgYsdIm9KnIIr8GRpFxiC32vaM7dXIYnT1o0QzUqMssiktKi4ytbMu%2Bda89Y1SydyopCVfofCLa2ZWY0n1KVlMq%2F9UXwtEmlkTdkUG2GoPkLRg4SpmnHPRlSpjoOiabUpsvvYuG6GNVsRo5Nk7qQqWSmA52ANU6ZO%2B2Jv9LTX0AvIu70J4Y%2FuTFyjY5SSDC7kgE0heRt7QSaLcV1C7oWc%2BenRsc7jTMefTtG%2FiuXGXc%2BaDtVbJSTVCfLuglOzpxhro4J6Ss2wSaONjyUzo9Llukcvl4z638fWtPY%2BWgZQs1JqURM1T0Y61Y%2BoxJxObkxtN0didNHP6qNJujfxd%2F8AGXk51jVg5Wow09kc9UhE1Jvb0dcmue3C5Zn9CwZHLIticmN3oLCnB7NLPjOX66qlGOw45E%2BGc55C1la9nJfHa6Z3I19RNUZ4sFz7t2VY5zkwW6cslPkYsm%2BTKmEm2RYqVqWTfI%2FHkMUWOg%2BDOxcdLFkX00xmn7ObjkacUjKxcrfCnyOikjJCY9TdCM2bVGLPjUkx0pmfLMJ%2Bhhy4Yxd1swdTt9p0c8rWjDKLlLZ1eK3%2FAK5%2FJGaMWoCJ4e6R0ngUkqG4unXdtG39pPrP%2Bdrjrpgl0tHWzdMnuOjLPHKMqK5806T14ryyrCkWscLNaxL2R4YrYf0hehHZj%2BIgfbEg%2FYepHYiOERqjZFibH7DGaeNVoVKDa0jc8VegXjoqdJvLk5ccovYo6mXE2uDJPBvSLllRZjMQKUHF7QIyQhCAFp1wMjkaWxRdgDu%2B1YLmwFJksAZGch%2BPK0%2BTIpUMjO2Fmnrq4c9qmxsuLRzsMkbITtVZl1zjSdGxlsbHkXjSexqaRnVwVuiO6I2q5Bb1ySdq1%2BhRX0DuRayANOhHY1UvZlWTfIXlFebTlxplQqdC%2FI37KbbFJh26GaXwVJDX%2BsCTRpKzsKtr0A5OxkpKtCpWy4VS%2FwBLT%2FRdMl0PE60QV7Y6DS9mWEtDUu6ibFStKkmW%2BBcINIbFOtmdXFRf4Lce6T%2BDkvpaikhaZMcKTtPRU5JSpMeqijLljUu4X6f42YacS8mRw9mfDlSjthTyqSaMLLraX4vHPudjlNGWDS4L7xWHK2xkmG5KjnqbvkfjbaIsVKbexsHZnsOEqfIqG3G6VknmT9md5VVCpz%2BMMBs5WylOvYpSI9hgMcy1PYtRdEVoA0RkRzEptEcgwCk2%2BCknLgidjcb9UP8AB%2Bs%2BTG1szZVejfki%2B4x9Qu1t0XxfqOp8c7LBRf6KbkNyyk3tCm5Hfz%2BOS%2FpmOdM6XSSWjkqTT4N3S5Loy83O8r8d%2Bu3B3HkknaEYpvtCcmcNjrlSRnzQUoux7doy5p9rK5%2FRXPy4qbpGaaaNmWadmPIuTu8d1ydQtuwSEbRpWaFNOmRyLTtBmHugg5LkNSZKRBX6J8HFv2Mi6FJhoy7jTmnRY6PoRBbNETCxrKZF7NOKTsRGI%2BCr0Z1ca4PgemqMam0gJ9V2kTm1VuNspIRNpmKfWN%2BxUuqf0uePpN7jVNRfsVKME7Mzztvkp5Gy5xYm9ytKklpBLIkY1kaKll%2FR%2Bml7yNc82jNPLbESy37A8iNufFjPrya0KZJZbRn8qJ5PwucM72O5EA8qIXhex8GvYxGHDk0rZrhJNEdc2HLo2rQNVyGmimiZVFSpvgVLEr4NHaU1suXE2a5%2BfHr%2FAKmGeNp3WjtTivZj6hRVmvPWs%2BpjnFDpb0kA4NLgtASUwo1ew0t8AC6ZVMd27L7EAILSpjfHH0U4pO7AGY5JbNEcsUY7VhRknyFmnLjb%2FQ1wMjnbMFjccla2TeTnTowk5RsjugMc1QxyTWjKzGm6Vcid0n7CbXwq0MLTrlhKdIU2C5P0GaW4e8mivKxUW3yEo2GSHo3kb4AlNvkpxdldrsJIVtRMJLQNVyU5VwMpRPQt3YSlZNsPwX6FNpmnDLWxUcbb4NEMdLYuqfMaMTTQU5JIVFqKpcgZJMyza03Ib5EWpGW5P0Mg37HeYXse5ITO5PYakuCnsU%2BHayzdPQKyOzRLGmtiMkO1aC86c6NhJsZXsRi9D5yUY6Ofr425C5UzRjyJRRilKy4zoVhyt7mgXPejKst%2Bw%2B8n1PWjyOiW2JjJDoTS%2BCsw5dMhFsdCCvYuErGp0rsm0xziktCqClO1yApbEa6I0k0RvVi5St8jhNMO1rSDpraMuOTTNcNrYUQUIOT4Az9MlcmjVjaa0HJd0aCXBZrhy6FZ9rVGaf8AxmZXT0egjj7JcaLlGJrz5%2Boi%2BLmvNw6Sa1KNDYY%2ByR1Jw%2F26VmXOknVbKvlvSZ45yKE0ohPIq5MyboXPJT5M5ztX7Y1%2BT4Zss%2B5uwVNP2RtMqT1qbdZ5xXJnmm%2BDZJp6Eyiq0dHHWMuudYZ8gv8AR%2BXC0%2B4zu2zpn1z34lBopVRNemOiXBWSyl%2F7LSv2Th6nc0HCWyu0uKaCyCWnxkNhIzptBwlsxvGtZ02wkNWXtRnxSXbsuTOe8%2FW06PedVsw9RmuWmXmdLkyyavbNvH45PrLvu%2FgnldlLJ9AbVguSR0erHa1Y5RfJJZIx9mPyUtMCU73Yv57T92r%2BjdATyd3szd36Rz%2BFTiRN603uJa%2BiHJlJseFrUpIp5EZ%2B5siv0GDWnyRIIt%2FEQeDV45KuTVhnumzl45NP8NmKa0LqaObjpRloJy0ZIZWh8Mia2Y3lrLo7daRW%2FhadsJNC%2FAVODZlyYW3s6LaYnI4%2Byueisc2XTtN0C8La2bW4%2FQJTSNJ1UWMUsTi9olP4aZZFXBnnlVlS6momia9FLZBklgSf4GwXFAAabL7VXJbil6Bd8gFp1pjItJik7V0HFewDVjn%2Bjlk%2BGXHjcnyPjimnsi4qaap2%2BCOV%2BgoYvo1Y18ItkXJWdr4DtGlwS9ASiOUrC1INTQLjTBf4H6Nw7uRLQpMtyFh6Y0nyD402UpBJh%2BBccSCUEmA8lewX1HbwLLT2RpUa4Lb1yZP6nQDztvbD1tHs2N%2FClfsyef8AS11H6P1Hs2pplNtCIZrGrImTYcsRyZPJJBwSk7DlCNa0K0YV5HWxM3bsdLHStCZUtB%2BiVSn2vRHNyQD5LW2Z3mNZV2Rui0qRTWjOxcq0wlJ%2FRZaYrDlPhIbFmVOhsclEWacrXjkxynoxRyr6F5f0m8q1q7rIpJIy%2Bb9I8n6LD2NLnrQHdsT5AlIeFp8J0zRHL6sw94UclNbFYcrq4nrTNMHXLObizUh6zk02vJLWjNOb4Aee1yKeS3yANb7Y2Zpwc3Y2LvTGxSr0OXCv1gyRcImHN3N3R2ckYvky58Sp0jTjv1R3xrjzyyjKthrqKiF1GJp3RlcaO6TnqOS281oeW%2BAnN0Zd%2FR0XpWwvEhzrRf6mtoz5MLizdjnFqipRTdkTv1uKvGxzWmvoKTs3uEfdCpxV6Np3rK84zbsJJjO0nb%2Bj1KlZabRNLlkcl9ENXbCjNoDuQLmkPNPcalmoks7Mbn8Bc3RP84r3p2TLJ%2BxTlfLFuTbBbZc5xFtpjl%2Bgt2%2BQSFYWoUWVQElkshNgEstMGi6ALLv4UT0AS2QhABKi0xsNewUn7DSfpAbTjkPi16MuO%2FY9OuCLFStUOA%2BELxTVUPVMxvxpPpUnL0Jydxs7LBeNNhOsF51y8loS5Ta0dWeCLu0ZcmBLg156lZ3mue4zfLFSjJPaOlGG6aKlhTXBXsWVixy1TDGZcCrQpd0XTKl1Ii0tFclpP4AUxbV8DWVQAuKa5Q2FLkqvwiQG24XGrRqhKLObF9qDhmaM7zq5XRpWHGjHjzN88Dnk1ozvKpR5X8Et0VObYvuZUhWiciRTYNjYMf4U%2Br8d8orxOxyaDVNkW4r10qGHVsqcEjQ5dq0Z52xS6dmM84inF2aJWA2ays7CezQLhvkc7oFpocpFPjQNMc0iaQ9AYKX0OWTt9gvI16KqwwNOLqElyaI5O7hmCMND8Sa9kdcxU6bNvkRnio7GxbaEZW23ZniyrsJMBJlq7J6mrlNTtDOxVoQkx0JP2ZXmrnULlBp8A0zSqZTjrgJNO0hJl7GJUR8cFeifYFk7v0iTb4Djgk1Yekg9i%2B5k7hrxtaovxKtj9IXsBTa9hrJ9FThXAvYr4tVO2nybK8tMz7JTF%2FIf0bYdQorkZHqjnJv2WpNEXxKnbp%2F0N%2ByLLZgjJ%2FRsZ7Ivjq506MMmg3npGBZKXJWTMnHkJ47aL3I1S6lN8gT6mNUc2WUF5W%2FZ0T%2FPGN87bklGSMeWILysqWVOJpx47yz67nQHpE7v0VKewXM3xjrTHK0xjzWjF3OilKRN4lVO7GqU36FOcmB3SopydFSSJt0XfIFyl9Bd%2FSvY8LRNsq9lf%2FpK2MI2%2FpP%2FANJRK9UAQrb9lkAJRKIU7sCXSJSRRACEIQAhCEAIQhGAUWQgBCEIAMjDYaj%2BG5dL%2BFrp38M73Gk5rD27Ditmt9M36Iuna9E3uD1pUE%2FRpx9xI4mvQ7HBojrqVpzBRLaGRx2GsZlelyMziJnjT3RveECeBvhDnQvLmvGk9ItQXw2Pp5e0R4KXBfuj1c%2BeJVozzxRfJ1JYRMsFvgvntF5cvxNPXA%2BGO0a%2F5fwKOBxeir2U5Ynh%2FCvD%2BHTjhvkN9OqJ%2FpivRyXj%2FAfHrg6r6b8BfTfgTyFeHLeLeieJ%2FTovpvwr%2Bb8K94XpWGMZJjldezUsH4GsP4Te4c5rE0yKOzc8C%2BA%2Fz%2FEHvD9WZQsJRo0LA0WsBN6gnLO9BxdIa8DKeBsPaU8sIlld8A9zb4ZpXTsYsFeg9pB62sfaqtgtR%2BGuWLYPhY51CvLM6S4FyqzW8L%2BFPp38HOoV5YWtlduzd%2FL%2BFrpfwfvC9axKAcMLfo2x6evQ2OGkK%2BRU4YFjaGRhSNrwaAeBon30%2FXCV%2FkqUVI0LFfovw%2FgvaHlZHhtBRwq6NSwsJYnfAr0c5rN4KLjhNqxWivE0T7K9WeOJLkNY4sd437RTxC0YCOGFFPp4MZ45IOMX7FarCf54rhF9lKkaEmWoWT7U5GOcAHDRulh90LeOvRc7TeWJ4U0B4Ub3hvgDwuxztPqxPCqB8f4bnhZXgfwqdleWF41QLho3%2FwA%2F4U%2BnfwfvC9awpNFu0tGz%2Bf8ACn0%2B%2BA9oMrC3L6C79s6UejskugaCeTmC8WuW4OgWjpvpHwA%2Bkp8FzyRN4rnUwXBs6T6b8K%2Fm%2FB%2B8T6VzfGyeI6X834T%2Bf8D%2Bg9HN8RPGdL%2BZVwWum%2FA%2FoPRzPG6J42dP%2Bf8ACvA%2Fgf0Ho5vi%2FCeI6X85X834P3Ho53j%2Bk8ejofzfhP5vwPeD1rn%2BMrxnR%2Flfwn8v4L3g9a53j0V4jpfzfhP5X8H7welc3xE8R0v5ifzfgv6D0rm%2BInhOl%2FN%2BE%2Fl1wH9B6VzfERYjpLpvwn8v4P8AoPRzfF%2BE8Xw6X8z%2BE%2FlD%2Bg9HN8WieI6X8v4T%2BV%2FA%2FoPSub4ieI6X834T%2BX8F%2FQejneEh0f5%2Fwgf0Ho6v88S1gRCHJrqxP50TwRIQNGIsES1hVkILRIOONINQRCCqoNQRfiRCEGp4RcsSIQqUUL6dPkn80PhCD1OBfTwoHwRIQe0sEsEUEsUSEA4niiU8ESEDSV4Ik8ESEFtPE%2FniRYIkIGjF%2BFEWGJCDJHhiX4YkIAV4EUsMSEDTwSwxLeKNcEIAwvwxsvwRIQNGK8ESeCJCC2li%2FBErwRIQNp4vwIixIhBlBrEi%2FCvhCCPAeKISwpohBhfiSL8aIQRiWJFvEiEJ0wvErK8aIQYX40X40QgUL8RSikQgtC6RfiiyEFTivGieFEIPSxTworxIhB6AuEfhHiRCDJPCqK8SIQAZCCQbhGiEM%2Bv1c%2FC3jj8AeFfCEL5qOgvCivDEhB7SxPDEngRCBtGJ4Yk8MSEDaMTwxZP50QgbRieCJPBEhA2jE8ESeCJCD0YnhiRYY%2FCEFoxPBH4Twx%2BEIG0YngiRYIkIGjE8MSfzohA0Ynhj8J4Y%2FCEDaMTwR%2BE%2FnRCBtGJ4I%2FCfzohA2jE8ESeCJCBoxPBH4QhA0Y%2F%2F2Q%3D%3D) no-repeat;
	height:166px;
	border-bottom:1px solid #ccc;
	overflow:hidden;
}
#header h1, #header h2 {
	color:#fff;
	padding:55px 0 0 75px;
	font-size:3em;
}	
#header h2 {
	font-size:1.2em;
	padding:0 0 0 65px;
	margin-top:-6px;
}	
#leftcol {
	float:left;
	width:490px;
}
#rightcol {
	width:240px;
	float:right;
}
#rightcol h1 {
	font-size:1.3em;
	padding-left:20px;
	font-weight:bold;
	font-family:helvetica, arial, sans-serif;
}	
img.portrait {
	background:#fff;
	padding:4px;
	border:1px solid #ccc;
	display:block;
	margin:10px auto 0 auto;
}	
#rightcol li {
	line-height:1.5em;
	list-style:none;
}	
#nav {
	list-style:none;
	padding-left:17px;
	margin:0 auto;
}
#nav li {
	background:#fff;
	width:210px;
	height:1.8em;
	line-height:1.8em;
	text-align:center;
	font-size:1.2em;
	margin:5px 0;
	border-bottom:1px solid #ccc;
	border-right:1px solid #ccc;
}	
#nav li a {
	color:#333;
	text-decoration:none;
	display:block;
	width:210px;
}	
#nav li a:hover {
	background:#4C83BA;
	color:#fff;
}	
#footer {
	text-align:center;
	background:#fff;
	clear:both;
	margin:0;
	border-top:1px solid #ccc;
}	
EOS;
global $admin_stylesheet;

/** Return the header for the admin pages, including any non-fatal errors. */
function admin_header() {
  global $error_cache;
  if (isset($error_cache) && $error_cache != '') {
    $error_cache = "$error_cache<br>";
  }
  global $admin_stylesheet;
  return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>Linkz Zerver</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />


<style>
' . $admin_stylesheet . '
</style>

</head>

<body>

<div id="container">
<div id="header"><h1>Linkz</h1><h2>The Linkz Zerver System</h2></div>


<div id="leftcol">' . $error_cache;
}

/** Return the admin navigation menu, includes standard icons for user group if
  menu_items isn't specified.
  @param menu_items (optional) A list of menu items to use.
  @return                      Navigation menu. */
function admin_menu($menu_items = NULL) {
  $output = "</div><div id=\"rightcol\">";
  if ($menu_items){
    $output .= $menu_items;
  } elseif (linkz_user::logged_in()) {
    global $script_name;
    $output .= "<h1>Navigation</h1>\n";
    $output .= "<ul id=\"nav\">\n";
    $output .= "<li><a href='$script_name'>Home</a></li>\n";
    $output .= "<li><a href='$script_name?do=linkz'>Manage Linkz</a></li>\n";
    $output .= "<li><a href='$script_name?do=config'>Settings</a></li>\n";
    $output .= "<li><a href='$script_name?do=logout'>Logout</a></li>\n";
    $output .= "</ul>\n";
  } else {
    $output .= linkz_user::login();
  }
  $output .= "<div style='height:200px;width:5px;'></div>"; // footer creep fix.
  $output .= "</div>";
  return $output;
}

/// Footer for administration pages.
$admin_footer = <<<EOF
<p id="footer">
Linkz Zerver, released under the GPL.  &copy; 2006 Robin Monks<br />
Theme by Kevin Cannon
</p>


</div>
</body>
</html>
EOF;

/// @} End of "Admin stlyes".

/////////  //    //  //////      /////    //    ////     //////
   //      //    //  //         //      //  //  //  //   //
   //      ////////  /////      //      //  //  //   //  /////
   //      //    //  //         //      //  //  //  //   //
   //      //    //  //////      /////    //    ////     //////  //  //  //
   
// Ensure SQLite is available.
if (!function_exists('sqlite_exec')) {
   die("<h1>SQLite is not enabled</h1>Please enable this extension manually in your php.ini file (called php_sqlite.dll or sqlite.so) or contact your hosting provider.");
}

// Let's open up a global DB and create initial tables
global $linkz_db;
$linkz_db = linkz_db::open_db();


if (!linkz_db::table_exists($linkz_db, 'user')) {
$linkz_db->queryExec('CREATE TABLE user (username VARCHAR(20), password VARCHAR(50))'); 
}
if (!linkz_db::table_exists($linkz_db, 'variable')) {
$linkz_db->queryExec('CREATE TABLE variable (name VARCHAR(20), value BLOB)'); 
}
if (!linkz_db::table_exists($linkz_db, 'sites')) {
$linkz_db->queryExec('CREATE TABLE sites (name VARCHAR(20), url BLOB, zerver BLOB, last_ping VARCHAR(20), last_pong VARCHAR(20), api VARCHAR(5), displays INTEGER(15), description VARCHAR(50), keywords BLOB, image BLOB)'); 
}


// The fun begins :)

// POST takes priority.
if(isset($_POST['do'])) {$todo=$_POST['do'];}
elseif(isset($_GET['do'])) {$todo=urldecode($_GET['do']);}
else {$todo='default';}
$output = "";

session_name('linkz');
session_start();

switch ($todo) {
  case 'ping':
    header('Content-Type: text/xml; charset=UTF-8');
    echo print_r_xml(linkz_zerver::pong());
    break;
    
  case 'linkz':
    linkz_user::require_logged_in();
    $output .= admin_header();
    $output .= manage_linkz();
    $output .= admin_menu();
    $output .= $admin_footer; 
    break;
    
  case 'login':
    $error_cache = "<p style='color:red;'><b><u><i>You must login to continue.</i></u></b></p>\n";
  case 'logout':
    linkz_user::logout();
  default:
    $output .= admin_header();
    $output .= "<h1>The \"$def_site_name\" Linkz Zerver</h1>";
    $output .= "<p>Linkz are a revolutionary new way to link exchange with other related sites.  Linkz are decentralized, context-sensitive and self-discovering.  Linkz can also be themed any way you want.  Linkz put you back in control of your link exchange.</p>
    <p>To link exchange with my site, you'll need to get the Linkz Zerver.  The Zerver is free software under the GPL in comes as a single file.  The entire Zerver is stored in one PHP and one Database flatfile.  After you download and run the Zerver, you just need to set your first link site to my URL, doing this will add your site name, descript and keywords to my DB, and mine to yours.  Thanks all there is to it!</p>
    <p>You can find out more about Linkz, and download the Linkz Zerver at the linkz homepage.</p>
    <p>Hope to see you soon!</p>";
    $output .= admin_menu();
    $output .= $admin_footer;    


}

echo $output;

